package org.sunspotworld.demo;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ToneGenerator;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.sensorboard.peripheral.ISwitch;
import com.sun.spot.sensorboard.peripheral.ISwitchListener;
import java.io.IOException;
import javax.microedition.io.Connector;
import javax.microedition.io.Datagram;
import javax.microedition.io.DatagramConnection;
import com.sun.spot.io.j2me.radiogram.RadiogramConnection;
import com.sun.spot.sensorboard.peripheral.LEDColor;
import java.util.*;

/**
 *
 * @author noluckmurphy
 */
public class AudioThread extends TimerTask implements ISwitchListener
{
    private EDemoBoard m_demo;
    private ToneGenerator m_toneGen;
    private ISwitch m_sw1, m_sw2;

    private static int m_Tones[] = {
        4545, 4290, 4050, 3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408
    };
    
    private com.sun.spot.util.Queue m_playQueue;
    
    private ITriColorLED [] m_leds;
    //private static int m_major[] = { 0, 2, 4, 5, 7, 9, 11, 12 };
    //private static int m_minor[] = { 0, 2, 3, 5, 7, 8, 10, 12 };
    //private static int m_cded[] = { 0, 2, 4, 2 };
    //private int m_notes[] = m_major;
    private String m_noteMode = "major";
    private int m_startValue = 0;
    private int m_startTempo = 1000;
    private int m_startRest = 80;
    private boolean m_useLog = false;
    private boolean m_useLightShow = false;
    private int m_typeInt=0;
    private PlanningThread m_planningThread;
    private final Object m_synchObject;
    private long m_nextMeasureTime;
 
    public AudioThread(int startValue, int startTempo, int startRest)
    {
        m_startValue = startValue;
        m_startTempo = startTempo;
        m_startRest = startRest;
        //m_notes = m_major;
        m_demo = EDemoBoard.getInstance();
        m_sw1 = m_demo.getSwitches()[EDemoBoard.SW1];
        m_sw2 = m_demo.getSwitches()[EDemoBoard.SW2];

        m_sw1.addISwitchListener(this);       // enable automatic notification of switches
        m_sw2.addISwitchListener(this);
        m_leds = m_demo.getLEDs();
        m_typeInt = 0;
        m_toneGen = new ToneGenerator(m_demo.getIOPins()[EDemoBoard.D3]);
        m_playQueue = new com.sun.spot.util.Queue();
        m_planningThread = new PlanningThread();
        m_synchObject = new Object();
    }
    
    
       
    public void run()
    {
        Timer timer = new Timer();
        m_planningThread.setPlayQueue(m_playQueue);
        m_planningThread.setSynchObject(m_synchObject);
        //m_planningThread.setTempo(m_startTempo);
        timer.schedule(m_planningThread, 0);
        
        int currentLED = 0;
        
        /*int randint = 0;
        //if(spot.name == "Master"){
            Random r = new Random();
            randint = Math.abs(r.nextInt()) % m_notes.length;
        //    }
        int nextID =1;
        int nextNote = m_notes[nextID]+ m_startValue;
        int nextTempo = m_startTempo;
        int nextRest = m_startRest;
        int thisID = 0;
        int thisNote = m_notes[thisID]+ m_startValue;
        int thisTempo = m_startTempo;
        int thisRest = m_startRest;*/
        
        while(true){
                playNote();
                /*
                //if(name == "Master"){
                    randint = Math.abs(r.nextInt()) % m_notes.length;
                    // Play the tone, already
                    if(m_typeInt==3){
                        nextID = randint;
                    }
                    else{
                        nextID = (thisID +1)% m_notes.length;
                    }
                    nextNote = m_notes[nextID] + m_startValue;
                    nextTempo = m_startTempo;
                    nextRest = m_startRest;
                    
                    m_leds[thisID].setOff();
                    thisID = nextID;
                    thisNote = nextNote;
                    thisTempo = nextTempo;
                    thisRest = nextRest;
                 //   }
               //else{
                //    recvNote();
               //     }
                */
                
                // Yay, pretty lights
                if(m_useLightShow)
                {
                    // TODO: Make this work w/o referencing m_notes[i] (will get 
                            // index outofbounds exception
                    
                    // Determine intensity from note level (higher = brighter)
                    // and dirtily "convert" it so that it is w/in [0 to 255]
                    //int brightness = (m_notes[i] + m_startValue) * 2;
                    //if(brightness > 255)
                    //    brightness = 255;
                    
                    //derka sherka
                    //int brightness = (int) (m_startRest * 2.5);
               
                    // Turn on the lights!
                    m_leds[currentLED].setOff();
                    currentLED = (currentLED + 1) % 5 + 1;//8;
                    m_leds[currentLED].setOn();
                    
                    
                    //Choose a color based on mote role
                    if(SunSpotAudio.m_role == 1)
                    {
                        m_leds[currentLED].setColor(LEDColor.RED);
                    }
                    else if(SunSpotAudio.m_role == 2)
                    {
                        m_leds[currentLED].setColor(LEDColor.MAGENTA);
                    }
                    else if(SunSpotAudio.m_role == 3)
                    {
                        m_leds[currentLED].setColor(LEDColor.YELLOW);
                    }
                    else //if(SunSpotAudio.m_role == 4)
                    {
                        m_leds[currentLED].setColor(LEDColor.TURQUOISE);
                    }
                    
                    // Choose a color based on scale type
                    /*if(m_noteMode.equalsIgnoreCase("major"))
                        m_leds[currentLED].setRGB(255, 0, 0);
                    else if(m_noteMode.equalsIgnoreCase("minor"))
                        m_leds[currentLED].setRGB(0, 0, 255);
                    else
                        m_leds[currentLED].setRGB(0, 255, 0);*/
                }
            
            
            /*if(m_useLightShow)
            {
                // clear the lights once we've played all the notes
                for(int i = 0; i < m_leds.length; i++)
                {
                    if(m_leds[i].isOn()){};
                }
            }*/
        }   
    }

    
    /**
     * Set the tone generator to play the next note in the play queue (removing
     * it from the queue in the process.)
     */
    void playNote()
    {
        if(m_playQueue.isEmpty())
        {
            System.out.println("Empty play queue!!");
        }
        Note note = (Note) m_playQueue.get();
        
        if(note.isMarker())
        {
            try
            {
                synchronized(m_synchObject)
                {
                    m_synchObject.notify();
                }
            }
            catch(IllegalMonitorStateException e)
            {
                if(m_useLightShow)
                {
                    m_leds[0].setColor(LEDColor.MAGENTA);
                    m_leds[0].setOn();
                    m_leds[1].setColor(LEDColor.MAGENTA);
                    m_leds[1].setOn();
                }
            }
            if(SunSpotAudio.m_role != SunSpotAudio.role_leader)
            {
                SunSpotAudio.broadcastLocalAV();
            }
        }
        else if(!note.isRest())
        {
            int octave;
            float averageAV = (PlanningThread.localAV.getValence() + PlanningThread.globalAV.getValence()) / 2.0f;
            if(averageAV < 0.3f)
            {
                octave = 0;
            }
            else if(averageAV < 0.7f)
            {
                octave = 1;
            }
            else
            {
                octave = 2;
            }
            m_toneGen.setPeriod(note2period(note.getPitchIndex() + octave * 12));
            m_toneGen.setDuration(note.getLength() * note.getOnTime()/100);
            m_toneGen.beep();
        }
        
        if(m_useLog)
        {
            System.out.println("Playing pitch " + Integer.toString(note.getPitchIndex())
                    + " for " + Integer.toString(note.getOnTime()) + "% of "
                    + Integer.toString(note.getLength()) + "ms.");
        }
        
        try 
        {
            // This might not be right - if environment gets read while we're 
            // sleeping we might not finish the note.
            //sendNote(nextNote, nextTempo, nextRest);
            Thread.sleep(note.getLength());  //System.currentTimeMillis();...?
        }
        catch (InterruptedException e)
        {
           System.out.println("exception");
            return;
        }
    }

    public void switchPressed(ISwitch sw) {

    }

    public void switchReleased(ISwitch sw) {

        int upOrDown_freq = (sw == m_sw1) ? 2 : -2;
        int upOrDown_synch = (sw == m_sw1) ? 50 : -50;
        int[] newTones = new int[12];

        ISwitch otherSwitch;
        if(sw == m_sw1)
        {
            otherSwitch = m_sw2;
        }
        else
        {
            otherSwitch = m_sw1;
        }
        
        if(otherSwitch.isClosed())
        {
            SunSpotAudio.m_synchOffset += upOrDown_synch;
        }
        else
        {
            for(int i = 0; i < m_Tones.length; i++)
            {
                newTones[i] = m_Tones[i] + upOrDown_freq;
            }

            synchronized(m_synchObject)
            {
                m_Tones = newTones;
            }
        }
    }
    
    
    /**
     * Given a pitch index, determines the on-period to send to
     * the tone generator.
     * 
     * @param note
     * @return
     */
    public static int note2period(int note)
    {
        int octave = note / 12;
        return m_Tones[note % 12] / (1 << octave);
        //multiply base periods by powers of 2 to get higher octaves.
    }
    
    
    /**
     * Return the local time at which the next measure will start playing.
     * 
     * @return local time in ms format
     */
    public long getNextMeasureTime()
    {
        return m_nextMeasureTime;
    }
    
    public void IsLogEnabled(boolean b)
    {
        m_useLog = b;
    }
    
    public void IsLightShowEnabled(boolean b)
    {
        m_useLightShow = b;
    }
    
    public void setStartPitch(int startValue)
    {
        m_startValue = startValue;
    }
    
    public void setStartTempo(int startTempo)
    {
        m_startTempo = startTempo;
    }
        
    public void setStartRest(int startRest)
    {
        m_startRest = startRest;
    }
    
    String recvNote()
    {
        String tmp = null;
        RadiogramConnection dgConnection = null;
        Datagram dg = null;

        try {
            dgConnection = (RadiogramConnection) Connector.open("radiogram://:37");
            // Then, we ask for a datagram with the maximum size allowed
            dg = dgConnection.newDatagram(dgConnection.getMaximumLength());
        } catch (IOException e) {
            System.out.println("Could not open radiogram receiver connection");
            e.printStackTrace();

        }

        while(true){
            try {
                dg.reset();
                dgConnection.receive(dg);
                tmp = dg.readUTF();
                int[] note = new int[4];

                int j =0;
                int k=0;
                for(int i =0; i<tmp.length(); i++){
                    if(tmp.charAt(i)==','){
                        note[k] = Integer.parseInt(tmp.substring(j, i-1));
                        j=i+1;
                        k++;
                        }
                }
                playNote();
                System.out.println("Received: " + tmp + " from " + dg.getAddress());
            } catch (IOException e) {
                System.out.println("Nothing received");
            }
        }
    }
    
    void sendNote(int note, int dur, int len)
    {
        DatagramConnection dgConnection = null;
        Datagram dg = null;
        try {
            // The Connection is a broadcast so we specify it in the creation string
            dgConnection = (DatagramConnection) Connector.open("radiogram://broadcast:37");
            // Then, we ask for a datagram with the maximum size allowed
            dg = dgConnection.newDatagram(dgConnection.getMaximumLength());
        } catch (IOException ex) {
            System.out.println("Could not open radiogram broadcast connection");
            ex.printStackTrace();
            return;
        }
        try {
            // We send the message (UTF encoded)
            dg.reset();
            dg.writeInt(note);
            dg.writeInt(dur);
            dg.writeInt(len);
            dg.writeInt(m_typeInt);
            dgConnection.send(dg);
            dgConnection.close();
            System.out.println("Broadcast is going through");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
